package think.in.java.concurrency.practise.exec8.ForNotify;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * Created by IntelliJ IDEA.
 * User: Richard
 * Date: 6/18/11
 * Time: 12:09 AM
 * To change this template use File | Settings | File Templates.
 */
public class Restrant implements Runnable {
    private static volatile int customersCount = 0;
    private List<Chef> chefs;
    private static volatile int leftMeat = 10;
    private int preparedFood = 0;
    private List<Customer> customers = Collections.synchronizedList(new LinkedList<Customer>());
    private List<Customer> waitingFoodCustomers = Collections.synchronizedList(new LinkedList<Customer>());
    private List<Customer> waitingDeliverCustomers = Collections.synchronizedList(new LinkedList<Customer>());
    private List<Food> foods = Collections.synchronizedList(new LinkedList<Food>());
    private List<Waiter> waiters;
    private CountDownLatch chefCountDown;
    private CountDownLatch waiterCountDown;
    private ReadWriteLock readWriteLockForCustomer = new ReentrantReadWriteLock();
    private ReadWriteLock readWriteLockForWaitingFoodCustomer = new ReentrantReadWriteLock();
    private ReadWriteLock readWriteLockForFood = new ReentrantReadWriteLock();
    private ReadWriteLock readWriteLockForWaitingDeliverCustomers = new ReentrantReadWriteLock();
    private Object waiterNotifier ;
    private Object chefNotifier;


    public Restrant() {

    }

    /**
     * use to start all the chef and waiter , and waiting for its finish
     *
     * @throws InterruptedException
     */
    public void restrantOpen() throws InterruptedException {
        setChefCountDown(new CountDownLatch(getChefs().size()));
        setWaiterCountDown(new CountDownLatch(getWaiters().size()));
        ExecutorService executor = Executors.newFixedThreadPool(getChefs().size() + getWaiters().size());
        for (Chef chef : this.getChefs()) {
            executor.execute(chef);
        }

        for (Waiter waiter : this.getWaiters()) {
            executor.execute(waiter);
        }

        peerGetReady(getChefCountDown(), getWaiterCountDown());
        System.out.println("!All the stuff are get ready !");

    }

    private boolean peerGetReady(CountDownLatch chefCountDown, CountDownLatch waiterCountDown) throws InterruptedException {
        chefCountDown.await();
        System.out.println("!All the chef are get ready !");
        waiterCountDown.await();
        System.out.println("!All the waiter are get ready !");
        return true;

    }


    public synchronized void customerComeIn(Customer customer) {
        System.out.println(customer + " Come in!");
        customers.add(customer);
        synchronized (Waiter.getNotifier()) {
            Waiter.getNotifier().notify();
        }

    }


    public void run() {
        try {
            restrantOpen();
            int count=0;
            while (!Thread.interrupted()) {
                if (leftMeat <= 0) {
                    throw new InterruptedException("Out of food!");
                }
                if(count<10)
                    this.customerComeIn(new Customer());
                count++;
            try {
                Thread.sleep(new Random().nextInt(50));
            } catch (InterruptedException e) {
                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            }
                Thread.sleep(10);
                Thread.yield();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
    }

    public static int getCustomersCount() {
        return customersCount;
    }

    public static void setCustomersCount(int customersCount) {
        Restrant.customersCount = customersCount;
    }

    public static int getLeftMeat() {
        return leftMeat;
    }

    public static void setLeftMeat(int leftMeat) {
        Restrant.leftMeat = leftMeat;
    }

    public List<Chef> getChefs() {
        return chefs;
    }

    public void setChefs(List<Chef> chefs) {
        this.chefs = chefs;
    }

    public int getPreparedFood() {
        return preparedFood;
    }

    public void setPreparedFood(int preparedFood) {
        this.preparedFood = preparedFood;
    }

    public List<Customer> getCustomers() {
        try {
            readWriteLockForCustomer.readLock().lock();
            return customers;
        } finally {
            readWriteLockForCustomer.readLock().unlock();
        }


    }

    public void setCustomers(List<Customer> customers) {
        try {
            readWriteLockForCustomer.writeLock().lock();
            this.customers = customers;
        } finally {
            readWriteLockForCustomer.writeLock().unlock();
        }

    }

    public void putCustomers(Customer customer) {
        try {
            readWriteLockForCustomer.writeLock().lock();
            this.customers.add(customer);
        } finally {
            readWriteLockForCustomer.writeLock().unlock();
        }
    }

    public Customer popCustomers() {
        try {
            readWriteLockForCustomer.writeLock().lock();
            if (this.customers.size() > 0)
                return this.customers.remove(0);
            else {
                return null;
            }
        } finally {
            readWriteLockForCustomer.writeLock().unlock();
        }
    }

    public List<Customer> getWaitingFoodCustomers() {
        try {
            this.readWriteLockForWaitingFoodCustomer.readLock().lock();
            return waitingFoodCustomers;
        } finally {
            this.readWriteLockForWaitingFoodCustomer.readLock().unlock();
        }
    }

    public void setWaitingFoodCustomers(List<Customer> waitingFoodCustomers) {
        try {
            this.readWriteLockForWaitingFoodCustomer.writeLock().lock();
            this.waitingFoodCustomers = waitingFoodCustomers;
        } finally {
            this.readWriteLockForWaitingFoodCustomer.writeLock().unlock();
        }
    }

    public void putWaitingFoodCustomers(Customer waitingFoodCustomer) {
        try {
            this.readWriteLockForWaitingFoodCustomer.writeLock().lock();
            this.waitingFoodCustomers.add(waitingFoodCustomer);
        } finally {
            this.readWriteLockForWaitingFoodCustomer.writeLock().unlock();
        }

    }

    public Customer popWaitingFoodCustomers() {
        try {
            this.readWriteLockForWaitingFoodCustomer.writeLock().lock();
            if (this.waitingFoodCustomers.size() > 0)
                return this.waitingFoodCustomers.remove(0);
            else {
                return null;
            }
        } finally {
            this.readWriteLockForWaitingFoodCustomer.writeLock().unlock();
        }

    }

    public Boolean removeWaitingFoodCustomers(Customer customer) {
        try {
            this.readWriteLockForWaitingFoodCustomer.writeLock().lock();
            if (this.waitingFoodCustomers.size() > 0){
                 for(Customer tmpCustomer : this.waitingFoodCustomers){
                    if(customer.getCustomerNum()==tmpCustomer.getCustomerNum()){
                         return this.waitingFoodCustomers.remove(tmpCustomer);
                    }
                }
                return null;
            }
            else {
                return null;
            }
        } finally {
            this.readWriteLockForWaitingFoodCustomer.writeLock().unlock();
        }

    }


    public List<Customer> getWaitingDeliverCustomers() {
        try {
            this.readWriteLockForWaitingDeliverCustomers.readLock().lock();
            return waitingDeliverCustomers;
        } finally {
            this.readWriteLockForWaitingDeliverCustomers.readLock().unlock();
        }
    }

    public void setWaitingDeliverCustomers(List<Customer> customers) {
        try {
            this.readWriteLockForWaitingDeliverCustomers.writeLock().lock();
            this.waitingDeliverCustomers = customers;
        } finally {
            this.readWriteLockForWaitingDeliverCustomers.writeLock().unlock();
        }
    }

    public void putWaitingDeliverCustomers(Customer customers) {
        try {
            this.readWriteLockForWaitingDeliverCustomers.writeLock().lock();
            this.waitingDeliverCustomers.add(customers);
        } finally {
            this.readWriteLockForWaitingDeliverCustomers.writeLock().unlock();
        }

    }

    public Customer popWaitingDeliverCustomers() {
        try {
            this.readWriteLockForWaitingDeliverCustomers.writeLock().lock();
            if (this.waitingDeliverCustomers.size() > 0)
                return this.waitingDeliverCustomers.remove(0);
            else {
                return null;
            }
        } finally {
            this.readWriteLockForWaitingDeliverCustomers.writeLock().unlock();
        }

    }

    public Boolean removeWaitingDeliverCustomers(Customer customer) {
        try {
            this.readWriteLockForWaitingDeliverCustomers.writeLock().lock();
            if (this.waitingDeliverCustomers.size() > 0){
                 for(Customer tmpCustomer : this.waitingDeliverCustomers){
                    if(customer.getCustomerNum()==tmpCustomer.getCustomerNum()){
                         return this.waitingDeliverCustomers.remove(tmpCustomer);
                    }
                }
                return null;
            }
            else {
                return null;
            }
        } finally {
            this.readWriteLockForWaitingDeliverCustomers.writeLock().unlock();
        }


    }

    public List<Food> getFoods() {
        try {
            this.readWriteLockForFood.readLock().lock();
            return foods;
        } finally {
            this.readWriteLockForFood.readLock().unlock();
        }

    }

    public void setFoods(List<Food> foods) {
        try {
            this.readWriteLockForFood.writeLock().lock();
            this.foods = foods;
        } finally {
            this.readWriteLockForFood.writeLock().unlock();
        }
    }

    public void putFood(Food food) {
        try {
            this.readWriteLockForFood.writeLock().lock();
            foods.add(food);
        } finally {
            this.readWriteLockForFood.writeLock().unlock();
        }

    }

    public Food popFood() {
        try {
            this.readWriteLockForFood.writeLock().lock();
            if (foods.size() > 0)
                return foods.remove(0);
            else
                return null;
        } finally {
            this.readWriteLockForFood.writeLock().unlock();
        }
    }

    public List<Waiter> getWaiters() {
        return waiters;
    }

    public void setWaiters(List<Waiter> waiters) {
        this.waiters = waiters;
    }

    public CountDownLatch getChefCountDown() {
        return chefCountDown;
    }

    public void setChefCountDown(CountDownLatch chefCountDown) {
        this.chefCountDown = chefCountDown;
    }

    public CountDownLatch getWaiterCountDown() {
        return waiterCountDown;
    }

    public void setWaiterCountDown(CountDownLatch waiterCountDown) {
        this.waiterCountDown = waiterCountDown;
    }


    public ReadWriteLock getReadWriteLockForWaitingFoodCustomer() {
        return readWriteLockForWaitingFoodCustomer;
    }

    public void setReadWriteLockForWaitingFoodCustomer(ReadWriteLock readWriteLockForWaitingFoodCustomer) {
        this.readWriteLockForWaitingFoodCustomer = readWriteLockForWaitingFoodCustomer;
    }

    public ReadWriteLock getReadWriteLockForFood() {
        return readWriteLockForFood;
    }

    public void setReadWriteLockForFood(ReadWriteLock readWriteLockForFood) {
        this.readWriteLockForFood = readWriteLockForFood;
    }


    public Object getWaiterNotifier() {
        return waiterNotifier;
    }

    public void setWaiterNotifier(Object waiterNotifier) {
        this.waiterNotifier = waiterNotifier;
    }

    public Object getChefNotifier() {
        return chefNotifier;
    }

    public void setChefNotifier(Object chefNotifier) {
        this.chefNotifier = chefNotifier;
    }
}


